package com.feituan.wx.advice;

import com.feituan.core.error.SystemCode;
import com.feituan.core.exception.BusinessException;
import com.feituan.core.exception.RequestInvalidException;
import com.feituan.core.exception.SystemException;
import com.feituan.core.pojo.response.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;

/**
 * 统一异常处理
 */
@RestControllerAdvice
@Component
public class ExceptionControllerAdvice {

    private final static Logger logger = LoggerFactory.getLogger(ExceptionControllerAdvice.class);

    /**
     * 处理系统异常
     */
    @ExceptionHandler(value = SystemException.class)
    public Response<?> handleException(SystemException ex) {
        logger.error("system exception occurred, class name : {}", ex.getClass().getName(), ex);
        return Response.failed(ex.getSystemCode(), ex.getExtMessage());
    }

    /**
     * 处理业务异常
     */
    @ExceptionHandler(value = BusinessException.class)
    public Response<?> handleException(BusinessException ex) {
        logger.error("business exception occurred, class name : {}", ex.getClass().getName(), ex);
        return Response.failed(ex.getSystemCode(), ex.getExtMessage());
    }


    /**
     * 请求类型异常处理器
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Response<?> handleException(HttpRequestMethodNotSupportedException ex) {
        logger.error("HttpRequestMethodNotSupportedException occurred, class name : {}", ex.getClass().getName(), ex);
        final Response<?> response = new Response<>();
        response.setCode(String.valueOf(SystemCode.UNEXPECTED.getCode()));
        response.setMsg(String.format("请求类型%s不支持", ex.getMethod()));
        return response;
    }

    /**
     * 忽略参数异常处理器
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Response<?> handleException(MissingServletRequestParameterException ex) {
        logger.error("MissingServletRequestParameterException occurred, class name : {}", ex.getClass().getName(), ex);
        final Response<?> response = new Response<>();
        response.setCode(String.valueOf(SystemCode.UNEXPECTED.getCode()));
        response.setMsg(String.format("请求参数%s不能为空", ex.getParameterName()));
        return response;
    }

    /**
     * 缺少请求体异常处理器
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Response<?> handleException(HttpMessageNotReadableException ex) {
        logger.error("HttpMessageNotReadableException occurred, class name : {}", ex.getClass().getName(), ex);
        final Response<?> response = new Response<>();
        response.setCode(String.valueOf(SystemCode.UNEXPECTED.getCode()));
        response.setMsg("请求参数错误");
        return response;
    }

    /**
     * 处理请求参数格式错误@RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Response<?> handleException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        // 目前只返回第一个校验错误信息，可以自定义错误信息
        String detailMessage = null;
        String tipMessage = null;
        List<ObjectError> objectErrors = bindingResult.getAllErrors();
        if (!CollectionUtils.isEmpty(objectErrors)) {
            ObjectError objectError = objectErrors.iterator().next();
            if (objectError instanceof FieldError) {
                FieldError fieldError = (FieldError) objectError;
                detailMessage = fieldError.getField() + fieldError.getDefaultMessage();
            } else {
                detailMessage = objectError.getObjectName() + objectError.getDefaultMessage();
            }
            tipMessage = objectError.getDefaultMessage();
        }
        logger.error("MethodArgumentNotValidException occurred, error class : {}, error detail : {} ", ex.getClass().getName(), detailMessage, ex);
        return Response.failed(SystemCode.ILLEGAL_PARAM, tipMessage);
    }

    /**
     * 处理请求参数格式错误@RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public Response<?> handleException(ConstraintViolationException ex) {
        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        String errorMsg = null;
        if (!CollectionUtils.isEmpty(violations)) {
            ConstraintViolation<?> violation = violations.iterator().next();
            errorMsg = violation.getMessage();
        }
        logger.error("ConstraintViolationException occurred, class name : {}, error message : {} ",
                ex.getClass().getName(), errorMsg, ex);
        return Response.failed(SystemCode.ILLEGAL_PARAM, errorMsg);
    }

    /**
     * 处理Get请求中验证路径中请求实体校验失败后抛出的异常是BindException
     */
    @ExceptionHandler(RequestInvalidException.class)
    public Response<?> handleException(RequestInvalidException ex) {
        return Response.failed(SystemCode.ILLEGAL_PARAM);
    }

    /**
     * 处理Exception异常
     */
    @ExceptionHandler(value = Exception.class)
    public Response<?> handleException(Exception ex) {
        logger.error("Unexpected occurred, class name : {}", ex.getClass().getName(), ex);
        return Response.failed(SystemCode.UNEXPECTED, ex.getMessage());
    }
}
